package cn.rocksolid.sport.common.utils;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import cn.rocksolid.sport.common.error.RSE;
import cn.rocksolid.sport.common.error.RockSolidException;

/**
 * @author Axl Zhao
 * @email axl.zhao@163.com
 */
public final class CodecUtils {

  private static final String AES_ALGORITHM = "DES";

  private static final SecretKey DEFAULT_AES_SECRET_KEY = genDesKey("4rfv^YHN_ROCKSOLID_^YHN*IK<");

  private static final String[] HEX_DIGITS =
          {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};

  private CodecUtils() {
  }

  public static String md5(String str) {
    return checksum(str, "MD5");
  }

  public static String md5(String str, Charset charset) {
    return checksum(str, "MD5", charset);
  }

  public static String sha1(String str) {
    return checksum(str, "SHA-1");
  }

  public static String sha256(String str) {
    return checksum(str, "SHA-256");
  }

  public static String sha384(String str) {
    return checksum(str, "SHA-384");
  }

  public static String sha512(String str) {
    return checksum(str, "SHA-512");
  }

  public static byte[] md5(byte[] data) {
    return checksum(data, "MD5");
  }

  public static byte[] sha1(byte[] data) {
    return checksum(data, "SHA-1");
  }

  public static byte[] sha256(byte[] data) {
    return checksum(data, "SHA-256");
  }

  public static byte[] sha384(byte[] data) {
    return checksum(data, "SHA-384");
  }

  public static byte[] sha512(byte[] data) {
    return checksum(data, "SHA-512");
  }

  private static String checksum(String str, String algorithm) {
    return byteArrayToHexString(checksum(str.getBytes(), algorithm));
  }

  private static String checksum(String str, String algorithm, Charset charset) {
    return byteArrayToHexString(checksum(str.getBytes(charset), algorithm));
  }

  private static byte[] checksum(byte[] data, String algorithm) {
    try {
      MessageDigest md = MessageDigest.getInstance(algorithm);
      return md.digest(data);
    } catch (NoSuchAlgorithmException nsae) {
      throw new RockSolidException(RSE.INVALID_CHECKSUM_ALGORITHM);
    }
  }

  public static String byteArrayToHexString(byte[] bytes) {
    AssertUtils.isArray(ArrayUtils.toObject(bytes), 1, null, null);
    StringBuilder buf = new StringBuilder(bytes.length * 2);

    for (byte aByte : bytes) {
      buf.append(HEX_DIGITS[(aByte >> 4) & 0x0f]);
      buf.append(HEX_DIGITS[aByte & 0x0f]);
    }
    return buf.toString();
  }

  public static String aes3encrypt(String str) {

    return aes3encrypt(str, DEFAULT_AES_SECRET_KEY);
  }

  public static String aes3encrypt(String str, String key) {
    return aes3encrypt(str, genDesKey(key));
  }

  public static String aes3decrypt(String str) {
    return aes3decrypt(str, DEFAULT_AES_SECRET_KEY);
  }

  public static String aes3decrypt(String str, String key) {
    return aes3decrypt(str, genDesKey(key));
  }

  private static String aes3encrypt(String str, SecretKey secretKey) {
    if (null == secretKey || StringUtils.isBlank(str)) {
      throw new RockSolidException(RSE.INVALID_CHECKSUM_ALGORITHM);
    }
    try {
      Cipher encryptCipher = Cipher.getInstance(AES_ALGORITHM);
      encryptCipher.init(Cipher.ENCRYPT_MODE, secretKey);
      return Base64.encodeBase64String(encryptCipher.doFinal(str.getBytes()));
    } catch (Exception exp) {
      throw new RockSolidException(RSE.INVALID_CHECKSUM_ALGORITHM);
    }
  }

  private static String aes3decrypt(String str, SecretKey secretKey) {
    if (null == secretKey || StringUtils.isBlank(str)) {
      throw new RockSolidException(RSE.INVALID_CHECKSUM_ALGORITHM);
    }
    try {
      Cipher decryptCipher = Cipher.getInstance(AES_ALGORITHM);
      decryptCipher.init(Cipher.DECRYPT_MODE, secretKey);

      return new String(decryptCipher.doFinal(Base64.decodeBase64(str)));
    } catch (Exception exp) {
      throw new RockSolidException(RSE.INVALID_CHECKSUM_ALGORITHM);
    }
  }

  private static SecretKey genDesKey(String key) {
    try {
      MessageDigest md = MessageDigest.getInstance("MD5");
      return SecretKeyFactory.getInstance(AES_ALGORITHM).generateSecret(new DESKeySpec(md.digest(key.getBytes())));
    } catch (Exception exp) {
      throw new RockSolidException(RSE.INVALID_CHECKSUM_ALGORITHM);
    }
  }

  public static String hMacSha256(String str, String key) {
    if (StringUtils.isBlank(key) || StringUtils.isBlank(str)) {
      throw new RockSolidException(RSE.INVALID_CHECKSUM_ALGORITHM);
    }
    try {
      Mac hMacSha256 = Mac.getInstance("HmacSHA256");
      SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
      hMacSha256.init(secretKeySpec);
      return byteArrayToHexString(hMacSha256.doFinal(str.getBytes(StandardCharsets.UTF_8))).toUpperCase();
    } catch (Exception exp) {
      throw new RockSolidException(RSE.INVALID_CHECKSUM_ALGORITHM);
    }
  }
}
